/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
 */

package com.huawei.rpc.client;

import com.huawei.rpc.client.discovery.IDiscovererService;
import com.huawei.rpc.client.network.NetWorkClient;
import com.huawei.rpc.common.info.ServiceInfo;
import com.huawei.rpc.common.prorocol.MessageProtocol;
import com.huawei.rpc.common.prorocol.Request;
import com.huawei.rpc.common.prorocol.Response;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.StringUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;


/**
 * @Author Lijl
 * @ClassName ClientProxyFactory
 * @Description 客户端代理工厂：用于创建远程服务代理类；封装编组请求、请求发送、编组响应等操作
 * @Date 2022/2/14 17:13
 * @Version 1.0
 */
public class ClientProxyFactory {
    @Getter
    @Setter
    private IDiscovererService iDiscovererService;
    @Getter
    @Setter
    private Map<String, MessageProtocol> supportMessageProtocols;
    @Getter
    @Setter
    private NetWorkClient netWorkClient;


    /**
     * @Author lijl
     * @MethodName getProxy
     * @Description 通过Java动态代理获取服务代理类
     * @Date 14:14 2022/2/15
     * @Version 1.0
     * @param clazz 被代理类Class
     * @return: T 服务代理类
    **/
    public <T> T getProxy(Class<T> clazz,String version){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, new ClientInvocationHandler(clazz,version));
    }

    /**
     * @Author Lijl
     * @ClassName ClientProxyFactory
     * @Description 客户端服务代理类invoke函数细节实现
     * @Date 2022/2/15 14:31
     * @Version 1.0
     */
    private class ClientInvocationHandler implements InvocationHandler {

        private Class<?> aClass;
        private String version;

        public ClientInvocationHandler(Class<?> aClass,String version){
            super();
            this.aClass = aClass;
            this.version = version;
        }

        private Random random = new Random();
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if ("toString".equals(methodName)){
                return proxy.getClass().toString();
            }
            if (("hashCode").equals(methodName)){
                return 0;
            }
            //1、获取服务信息
            String serviceName = this.aClass.getName();
            if (StringUtils.hasLength(this.version)){
                serviceName += ":"+version;
            }
            List<ServiceInfo> serviceInfoList = iDiscovererService.getService(serviceName);

            if (serviceInfoList==null||serviceInfoList.isEmpty()){
                throw new ClassCastException("No provider available");
            }
            //多个服务的话随机获取一个
            ServiceInfo serviceInfo = serviceInfoList.get(random.nextInt(serviceInfoList.size()));

            //2、构造request对象
            Request request = new Request();
            request.setServiceName(serviceInfo.getName());
            request.setMethod(methodName);
            request.setParameterTypes(method.getParameterTypes());
            request.setParameters(args);

            //3、请求协议编组
            //获取该方法对应的协议
            MessageProtocol messageProtocol = supportMessageProtocols.get(serviceInfo.getProtocol());
            //编组请求
            byte[] data = messageProtocol.marshallingRequest(request);

            //4、条用网络层发送请求
            byte[] requestData = netWorkClient.sendRequest(data, serviceInfo);

            //5、解组响应信息
            Response response = messageProtocol.unmarshallingResponse(requestData);

            // 6、处理结果
            if (response.getException()!=null){
                throw response.getException();
            }
            return response.getReturnValue();
        }
    }
}
